﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MatrixCalculator;
using ObjectBuilder;

namespace SimilarityScoring
{
    public class Similarity
    {
        public delegate void CalculateComplete();
        public event CalculateComplete CalculateCompleteEvent;
        private Calculator mc = new Calculator();
        private double threshold = 0.000;
        private int[] maxx = new int[2];
        double b;
        #region Property
        public double[,] GenScore
        {
            get;
            set;
        }
        public double[,] AscScore
        {
            get;
            set;
        }
        public double[,] TotalScore
        {
            get;
            set;
        }
        public double Threshold
        {
            get { return threshold; }
            set { if (value <= 0) threshold = 0.000; else threshold = value; }
        }
        public bool IsGenConvergence
        {
            get;
            set;
        }
        public bool IsAscConvergence
        {
            get;
            set;
        }
        public Parser parser { get; set; }
        public double[,] PatternGen { get; set; }
        public double[,] PatternAsc { get; set; }
        private bool isConvergate = false;
        #endregion
        public void calculate(Parser p)
        {
            mc = new Calculator();
            GenScore = _calculate(p.Gen, Observer.Gen);
            Console.WriteLine("Vysledna matice Generalizace");
            mc.print(GenScore);
            AscScore = _calculate(p.Asc, Observer.Asc);
            Console.WriteLine("Vysledna matice Asociace");
            mc.print(AscScore);
            Console.WriteLine("res");
            TotalScore = mc.normScores(GenScore, AscScore);
            Console.WriteLine();
            mc.print(TotalScore);
            Console.WriteLine("----------------------");
        }

        public void calculate(Parser parser,double[,] patternGen, double[,] patternAsc)
        {
            GenScore = _calculate(parser.Gen, patternGen);
            IsGenConvergence = isConvergate;
            AscScore = _calculate(parser.Asc, patternAsc);
            IsAscConvergence = isConvergate;
            GenScore = control(GenScore);
            AscScore = control(AscScore);
            TotalScore = mc.normScores(this.GenScore, this.AscScore);
            if (CalculateCompleteEvent != null)
            {
                CalculateCompleteEvent();
            }
        }

        private double[,] control(double[,] matrix)
        {
            for (int i = 0; i < matrix.GetLength(0); i++)
            {
                for (int j = 0; j < matrix.GetLength(1); j++)
                {
                    if (double.IsInfinity(matrix[i,j])||
                        double.IsNaN(matrix[i,j])||
                        double.IsNegativeInfinity(matrix[i,j])||
                        double.IsPositiveInfinity(matrix[i,j]))
                        matrix[i, j] = 0;
                }
            }
            return matrix;
        }

        public double GetScore()
        {
            int notNull = 0 ;
            double count = 0;
            bool isNotNull = false;
            for (int i = 0; i < TotalScore.GetLength(0); i++)
            {
                isNotNull = false;
                for (int j = 0; j < TotalScore.GetLength(1); j++)
                {
                    if (TotalScore[i, j] != 0)
                    {
                        count += TotalScore[i, j];
                        isNotNull = true;
                    }
                }
                if (isNotNull) notNull++;
            }
            //Console.WriteLine(count);
            //Console.WriteLine(notNull);
            return count / notNull;
        }

        public double MaxScore()
        {
            double[,] tmpTotalScore = TotalScore;
            double sum = 0;
            int x;
            if (TotalScore.GetLength(1) > TotalScore.GetLength(0)) x = TotalScore.GetLength(0); else x = TotalScore.GetLength(1);
            for (int i = 0; i < x; i++)
            {
                sum += getmax(tmpTotalScore);
                tmpTotalScore = rem(tmpTotalScore, maxx[0], maxx[1]);
            }
            double avg = sum / TotalScore.GetLength(1);
            return avg;
        }

        public double getmax(double[,] A)
        {
            double max = -1;
            int l = -1; int k = -1;
            for (int i = 0; i < A.GetLength(0); i++)
            {
                for (int j = 0; j < A.GetLength(1); j++)
                {
                    if (A[i, j] >= max)
                    {
                        max = A[i, j];
                        l = i;
                        k = j;
                    }
                }
            }
            maxx[0] = l; maxx[1] = k;
            return max;
        }

        public double[,] rem(double[,] A, int iRow, int jCol)
        {
            double[,] new_matrix = new double[A.GetLength(0) - 1, A.GetLength(1) - 1];
            bool r = false;
            bool c = false;
            for (int i = 0; i < A.GetLength(0); i++)
            {
                c = false;
                if (i == iRow) { r = true; continue; }
                for (int j = 0; j < A.GetLength(1); j++)
                {
                    if (j == jCol) { c = true; continue; }
                    if (r && !c) new_matrix[i - 1, j] = A[i, j];
                    else if (c && !r) new_matrix[i, j - 1] = A[i, j];
                    else if (r && c) new_matrix[i - 1, j - 1] = A[i, j];
                    else new_matrix[i, j] = A[i, j];
                }
            }
            return new_matrix;
        }

        private double[,] _calculate(double[,] Code, double[,] Pattern)
        {

            double[,] MatrixRes = new double[Code.GetLength(0), Pattern.GetLength(0)];
            double[,] MatrixTemp = new double[Code.GetLength(0), Pattern.GetLength(0)];
            double[,] TempMatrix = new double[Code.GetLength(0), Pattern.GetLength(0)];
            TempMatrix = mc.fill(1, MatrixRes);
            int k = 0;
            isConvergate = false;
            while(true)
            {
                double[,] MatrixTemp1 = mc.mul(mc.mul(Code, MatrixRes), mc.transpone(Pattern));
                double[,] MatrixTemp2 = mc.mul(mc.mul(mc.transpone(Code), MatrixRes), Pattern);
                MatrixTemp = mc.add(MatrixTemp1,MatrixTemp2);
                MatrixRes = mc.div(MatrixTemp, mc._1norm(MatrixTemp));
                isConvergate = Convergence(MatrixRes, TempMatrix);
                TempMatrix = MatrixRes;

                if (k == 200000 || isConvergate)
                {
                    break;
                }
                else
                {
                    k++;
                }
            } 
            return MatrixRes;
        }

        public bool Convergence(double[,] A, double[,] B)
        {
            for (int i = 0; i < A.GetLength(0); i++)
            {
                for (int j = 0; j < A.GetLength(1); j++)
                {
                    b = Math.Abs(Math.Round(A[i, j], 5) - Math.Round(B[i, j], 5));
                    if (Math.Abs(Math.Round(A[i, j], 5) - Math.Round(B[i, j], 5)) > threshold)
                        //if (Math.Abs(A[i, j] - B[i, j]) < 0.01D)
                        return false;
                }
            }
            return true;
        }

        public string ToString(double[,] matrix)
        {
            StringBuilder sb = new StringBuilder();
            //sb.Append(matrix.GetLength(1));
            for (int i = -2; i < matrix.GetLength(1); i++)
            {
                if (i < 0) sb.Append("   ");
                for (int j = -2; j < matrix.GetLength(1); j++)
                {
                    if (j > 0 && i >= 0)
                    {
                        if (matrix[i, j] == 1)
                        {
                            sb.Append("  " + matrix[i, j].ToString());
                        }
                        else
                        {
                            sb.Append("   ");
                        }
                    }

                    if (j == 0 && i >= 0)
                    {
                        if (matrix[i, j] == 1)
                        {
                            if (i < 10) sb.Append("0" + i + "| " + matrix[i, j].ToString());
                            else sb.Append(i + "| " + matrix[i, j].ToString());
                        }
                        else
                        {
                            if (i < 10) sb.Append("0" + i + "|  ");
                            else sb.Append(i + "|  ");
                        }
                    }
                    if (i < 0 && j >= matrix.GetLength(1) - 2) break;
                    if (i == -2)
                    {
                        if (j < 8) sb.Append("0" + (j + 2).ToString() + " ");
                        else sb.Append((j + 2).ToString() + " ");
                    }
                    if (i == -1) sb.Append("===");
                }
                sb.Append("\n");
            }
            return sb.ToString();
        }
    }
}
